//  the data that powers the bar chart, a simple array of numeric values
// import * as d3 from "./d3";

import { gotData } from "./main.js";

// const SERVER_HOST = "http://www.devecor.cn";
const SERVER_HOST = "http://localhost";
//  the size of the overall svg element
let height = 600,
    width = 750,

    //  the width of each bar and the offset between each bar
    barWidth = 3,
    barOffset = 5,
    margin = 70;

var visibility_t = "GONE";  // temperature line
var visibility_h = "GONE";  // humidity line
var visibility_f = "GONE";  // fit line
var visibility_p = "GONE";  // predict line

const data_table = d3.select('#data_table');

data_table.append('svg')
    .attr('width', width)
    .attr('height', height)
    .style('background-color', 'transparent')
    .append('g')
    .attr('id', 'main_field');

class DTM {
    constructor(dtm) {
        this.year = dtm.year;
        this.month = dtm.month;
        this.day = dtm.day;
        this.hour = dtm.hour;
        this.minute = dtm.minute;
        this.second = dtm.second;
        this.microsecond = dtm.microsecond;
    }
    toString() {
        return this.year + "/" + this.month + "/" +
            this.day + "/" + this.hour + "/" + this.minute +
            "/" + this.second + "/" + this.microsecond
    }
    toBuildInDate() {
        return new Date(this.year, this.month - 1, this.day,
            this.hour, this.minute, this.second);
    }
}

class Data {
    constructor(data) {
        this.temperature = data.temperature;
        this.humidity = data.humidity;
        this.percentage = data.percentage;
        this.datetime = (new DTM(data.datetime)).toBuildInDate();
    }
}

setInterval(request_data, 1000);

function render() {

    let render_data;
    if (data != null) {
        render_data = data;
    } else {
        alert("don't invoke render before data has gotten");
        return;
    }
    d3.selectAll('path').remove();

    let scale_x = d3.scaleTime()
        .domain([new DTM(data[0].datetime).toBuildInDate(),
        new DTM(data[data.length - 1].datetime).toBuildInDate()])
        .range([0, width - 2 * margin]);
    let scale_y = d3.scaleLinear()
        .domain([0, 10])
        .range([0, height - 2 * margin]);

    let percentage_charts = d3.select('#main_field').selectAll('.percentage_chart').data(render_data);
    let percentage_dots = d3.select('#main_field').selectAll('.percentage_dot').data(render_data);

    percentage_charts.enter().append('rect')
        .merge(percentage_charts)
        .attr('class', 'percentage_chart')
        .attr('fill', '#909399')
        .attr('fill-opacity', 0.3)
        .attr('width', barWidth)
        .attr('height', function (data) {
            return scale_y(new Data(data).percentage);
        })
        .attr('x', function (d) {
            // return i * (barWidth + barOffset) + margin;
            return scale_x(new Data(d).datetime) + margin - 0.5 * barWidth;
        })
        .attr('y', function (d) {
            return height - margin - scale_y(new Data(d).percentage);
        })
        .exit().remove();

    percentage_dots.enter().append('circle')
        .merge(percentage_dots)
        .attr('class', 'percentage_dot')
        .attr('fill', function (d) {
            return dot_color(new Data(d).percentage / 5.0);
        })
        .attr('cx', function (d) {
            return scale_x(new Data(d).datetime) + margin;
        })
        .attr('cy', function (d) {
            return height - margin - scale_y(new Data(d).percentage);
        })
        .attr('r', 0.8 * barWidth)
        .exit().remove();

    let percentage_line = d3.line()
        .x(function (d) {
            return scale_x(new Data(d).datetime) + margin;
        })
        .y(function (d) {
            return height - scale_y(new Data(d).percentage) - margin;
        });
    d3.select('#main_field')
        .append('path')
        .attr('id', 'percentage_line')
        .attr('stroke', "#000000")
        .attr('stroke-width', '2')
        .attr('stroke-opacity', 0.3)
        .attr('stroke-linejoin', 'round')
        .attr('fill', 'none')
        .attr('d', percentage_line(data));
}

function renderTempLine(visibility) {

    if (!visibility) {
        d3.select('#temperature_line').remove();
        d3.selectAll('.axis2').remove();
        return null;
    }

    let scale_x = d3.scaleTime()
        .domain([new DTM(data[0].datetime).toBuildInDate(),
        new DTM(data[data.length - 1].datetime).toBuildInDate()])
        .range([0, width - 2 * margin]);
    let scale_y2 = d3.scaleLinear()
        .domain([-10, 45])
        .range([0, height - 2 * margin]);

    let temperature_line = d3.line()
        .x(function (d) {
            return scale_x(new Data(d).datetime) + margin;
        })
        .y(function (d) {
            return height - scale_y2(new Data(d).temperature) - margin;
        });
    d3.select('#main_field')
        .append('path')
        .attr('id', 'temperature_line')
        .attr('stroke', '#E6A23C')
        .attr('stroke-width', '2')
        .attr('stroke-opacity', 0.5)
        // .attr('stroke-dasharray', '4, 4')
        .attr('stroke-linejoin', 'round')
        .attr('fill', 'none')
        .attr('d', temperature_line(data));

    d3.selectAll('.axis2').remove();
    const svg = d3.select('svg');
    let scale_y2_ = d3.scaleLinear()
        .domain([45, -10])
        .range([0, height - 2 * margin]);
    let axis_y2 = d3.axisRight(scale_y2_);
    svg.append('g')
        .attr('class', 'axis2')
        .attr('transform', "translate(" + (width - margin) + "," + margin + ")")
        .call(axis_y2);
    svg.append('g').attr('class', 'axis2')
        .append('text')
        .attr("y", 0.8 * margin)
        .attr("x", width - 1.2 * margin)
        .attr("fill", "#E6A23C")
        .attr("font-size", 15)
        .text("T/℃");
}

function renderHumiLine(visibility) {

    if (!visibility) {
        d3.select('#humidity_line').remove();
        d3.selectAll('.axis3').remove();
        return null;
    }

    let scale_x = d3.scaleTime()
        .domain([new DTM(data[0].datetime).toBuildInDate(),
        new DTM(data[data.length - 1].datetime).toBuildInDate()])
        .range([0, width - 2 * margin]);
    let scale_y = d3.scaleLinear()
        .domain([0, 100])
        .range([0, height - 2 * margin]);

    let humidity_line = d3.line()
        .x(function (d) {
            return scale_x(new Data(d).datetime) + margin;
        })
        .y(function (d) {
            return height - scale_y(new Data(d).humidity) - margin;
        });

    d3.select('#main_field')
        .append('path')
        .attr('id', 'humidity_line')
        .attr('stroke', '#409EFF')
        .attr('stroke-width', '2')
        .attr('stroke-opacity', 0.5)
        // .attr('stroke-dasharray', '8, 4')
        .attr('stroke-linejoin', 'round')
        .attr('fill', 'none')
        .attr('d', humidity_line(data));
    
    d3.selectAll('.axis3').remove();
    const svg = d3.select('svg');
    let scale_y3 = d3.scaleLinear()
        .domain([100, 0])
        .range([0, height - 2 * margin]);
    let axis_y3 = d3.axisRight(scale_y3);
    svg.append('g')
        .attr('class', 'axis3')
        .attr('transform', "translate(" + (width - 0.5*margin) + "," + margin + ")")
        .call(axis_y3);
    svg.append('g').attr('class', 'axis3')
        .append('text')
        .attr("y", 0.8 * margin)
        .attr("x", width - 0.5 * margin)
        .attr("fill", "#409EFF")
        .attr("font-size", 15)
        .text("H/c%");
}

function renderFitLine(mdata, visibility) {

    if (!visibility) {
        d3.select('#fit_line').remove();
        return null;
    }

    let scale_x = d3.scaleTime()
        .domain([new DTM(mdata[0].datetime).toBuildInDate(),
        new DTM(mdata[mdata.length - 1].datetime).toBuildInDate()])
        .range([0, width - 2 * margin]);
    let scale_y = d3.scaleLinear()
        .domain([0, 10])
        .range([0, height - 2 * margin]);

    let fit_line = d3.line()
        .x(function (d) {
            return scale_x((new DTM(d.datetime)).toBuildInDate()) + margin;
        })
        .y(function (d) {
            return height - scale_y(d.fitValue) - margin;
        });

    d3.select('#main_field')
        .append('path')
        .attr('id', 'fit_line')
        .attr('stroke', '#67C23A')
        .attr('stroke-width', '2')
        .attr('stroke-dasharray', '8, 4')
        .attr('stroke-linejoin', 'round')
        .attr('fill', 'none')
        .attr('d', fit_line(mdata));
}

function renderPredictLine(mdata, visibility) {

    if (!visibility) {
        d3.select('#predict_line').remove();
        return null;
    }

    let scale_x = d3.scaleTime()
        .domain([new DTM(mdata[0].datetime).toBuildInDate(),
        new DTM(mdata[mdata.length - 1].datetime).toBuildInDate()])
        .range([0, width - 2 * margin]);
    let scale_y = d3.scaleLinear()
        .domain([0, 1])
        .range([0, height - 2 * margin]);

    let predict_line = d3.line()
        .x(function (d) {
            return scale_x((new DTM(d.datetime)).toBuildInDate()) + margin;
        })
        .y(function (d) {
            return height - scale_y(d.predictValue) - margin;
        });

    d3.select('#main_field')
        .append('path')
        .attr('id', 'predict_line')
        .attr('stroke', '#F56C6C')
        .attr('stroke-width', '2')
        .attr('stroke-opacity', 0.5)
        .attr('stroke-dasharray', '8, 4')
        .attr('stroke-linejoin', 'round')
        .attr('fill', 'none')
        .attr('d', predict_line(mdata));
}

function renderAxis(Date_start, Date_end) {
    const svg = d3.select('svg');
    svg.selectAll('.axis').remove();

    let scale_x = d3.scaleTime()
        .domain([Date_start, Date_end])
        .range([0, width - 2 * margin]);
    let scale_y = d3.scaleLinear()
        .domain([10, 0])
        .range([0, height - 2 * margin]);

    let axis_x = d3.axisBottom(scale_x);
    let axis_y = d3.axisLeft(scale_y);

    svg.append('g')
        .attr("class", "axis")
        .attr('transform', "translate(" + margin + ", " + (height - margin) + ")")
        .call(axis_x);
    svg.append('g')
        .attr('class', 'axis')
        .attr('transform', "translate(" + margin + "," + margin + ")")
        .call(axis_y);
    svg.append('g').attr('class', 'axis')
        .append('text')
        .attr("y", 0.8 * margin)
        .attr("x", 0.5 * margin)
        .attr("fill","#303133")
        .attr("font-size",15)
        .text("Vc/c%");
    
}

let data;
let predictData;
let fitData;
async function request_data() {
    // const max_len = Math.trunc((width - 2 * margin) / (barOffset + barWidth));
    const max_len = 73;
    if (data == null || data.length === 0) {

        data = await d3.json(SERVER_HOST + "/main/request_data/" +
            max_len);
    } else {
        var temp_obj = data[data.length - 1].datetime;
        var temp_data = await d3.json(SERVER_HOST +
            "/main/request_data/" +
            max_len + "?cdt=" + new DTM(temp_obj).toString());

        let k = data.length + temp_data.length - max_len;
        if (k > 0) {
            data.splice(0, k);
        }
        data = data.concat(temp_data);
    }
    if (data != null && data.length > 0) {
        render();
        renderAxis(new DTM(data[0].datetime).toBuildInDate(),
            new DTM(data[data.length - 1].datetime).toBuildInDate());
        renderTempLine(visibility_t == "VISABLE");
        renderHumiLine(visibility_h == "VISABLE");
        gotData()
    }

    fitData = await d3.json(SERVER_HOST + '/main/fit_data/');
    predictData = await d3.json(SERVER_HOST + '/main/predict_data/');

    if (fitData != null && fitData.length > 0) {
        renderFitLine(fitData, visibility_f == "VISABLE");
    }
    if (predictData != null && predictData.length > 0) {
        renderPredictLine(predictData, visibility_p == "VISABLE");
    }
}

function requested_data_assert(data) {
    if (data == null || data.length <= 0) {
        return null;
    }
    if (data.temperature == null ||
        data.humidity == null ||
        data.percentage == null ||
        data.datetime == null) {
        alert("requested data format error");
        return null;
    } else {
        const dtm = data.datetime;
        if (dtm.year == null ||
            dtm.month == null ||
            dtm.day == null ||
            dtm.hour == null ||
            dtm.minute == null ||
            dtm.second == null ||
            dtm.microsecond == null) {

            alert("requested data format error");
        }
    }
}

function dot_color(percentage) {

    if (percentage > 1) {
        return '#FF0000'
    }
    if (percentage < 0) {
        return '#00FF00'
    }

    let f = function (d) {
        const s = d.toString(16);
        return s.length > 1 ? s : ("0" + s);
    };
    let r = Math.floor(percentage * 256);
    let g = (255 - r);
    let b = Math.floor(r * g / 255 * 4);
    return '#' + f(r) + f(g) + f(b);
}

function setVisibility(target, visibility) {

    switch (target) {
        case "TEMPERATURE":
            visibility_t = visibility;
            renderTempLine(visibility_t == "VISABLE");
            break;
        case "HUMIDITY":
            visibility_h = visibility;
            renderHumiLine(visibility_h == "VISABLE");
            break;
        case "FIT":
            visibility_f = visibility;
            renderFitLine(fitData, visibility_f == "VISABLE");
            break;
        case "PREDICT":
            visibility_p = visibility;
            renderPredictLine(predictData, visibility_p == "VISABLE");
            break;
    }
}

export {
    setVisibility,
    data
}
